package com.softer.wxzj.controller;

import com.softer.wxzj.common.aop.annotation.LogRecord;
import com.softer.wxzj.common.aop.enums.LogOperationType;
import com.softer.wxzj.common.aop.enums.LogOperationUnit;
import com.softer.wxzj.common.entity.CommonResult;
import com.softer.wxzj.common.enums.*;
import com.softer.wxzj.common.factory.AsyncFactory;
import com.softer.wxzj.common.flow.enums.FlowTypeEnum;
import com.softer.wxzj.common.util.CaptchaUtil;
import com.softer.wxzj.common.util.ServletUtils;
import com.softer.wxzj.common.util.ShiroUserUtil;
import com.softer.wxzj.common.util.StringUtils;
import com.softer.wxzj.entity.BaseOrganization;
import com.softer.wxzj.entity.BaseOwnerCommittee;
import com.softer.wxzj.entity.SysOperator;
import com.softer.wxzj.mapper.BaseOrganizationMapper;
import com.softer.wxzj.mapper.BaseOwnerCommitteeMapper;
import com.softer.wxzj.mapper.FlowAuditdetailMapper;
import com.softer.wxzj.service.LoginService;
import lombok.extern.slf4j.Slf4j;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.*;
import org.apache.shiro.authc.pam.UnsupportedTokenException;
import org.apache.shiro.subject.Subject;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

import javax.imageio.ImageIO;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.util.concurrent.ThreadFactory;

/**
 * LoginController class
 *
 * @author zcz
 * @date 2019/1/16
 */
@RestController
@Slf4j
public class LoginController {
    @Autowired
    private LoginService loginService;
    @Autowired
    private BaseOrganizationMapper organizationMapper;
    @Autowired
    private FlowAuditdetailMapper flowAuditdetailMapper;
    @Autowired
    private BaseOwnerCommitteeMapper baseOwnerCommitteeMapper;

    public static final String KEY_CAPTCHA = "KEY_CAPTCHA";

    /**
     * 退出
     *
     * @return
     */
    @GetMapping("/logout")
    public CommonResult logout() {
        Subject subject = SecurityUtils.getSubject();
        String userName = ShiroUserUtil.getUserName();
        if (subject != null) {
            subject.getSession().stop();
            subject.logout();
        }
        new SimpleThreadFactory().newThread(new AsyncFactory(ServletUtils.getRequest(), userName, "1", ShiroMessageEnum.SHIRO_AUTHC_LOGOUT_MESSAGE.getMsg())).start();
        return new CommonResult(ShiroResultEnum.SUCCESS.getCode(), ShiroMessageEnum.SHIRO_AUTHC_LOGOUT_MESSAGE.getMsg());
    }

    /**
     * 主管端用户登录
     *
     * @param operNo
     * @param operPwd
     * @param rememberMe
     * @return
     */
    @RequestMapping("/login")
    @LogRecord(description = "主管用户登录", operationUnit = LogOperationUnit.LOGIN, operationType = LogOperationType.LOGIN)
    public CommonResult login(String operNo,
                              String operPwd,
                              String captcha,
                              boolean rememberMe, HttpServletResponse response) throws IOException {
        return executeLogin(operNo, operPwd, captcha, rememberMe, response);
    }

    /**
     * 公共登录方法
     *
     * @param operNo
     * @param operPwd
     * @param captcha
     * @param rememberMe
     * @return
     */
    public CommonResult executeLogin(@RequestParam("operNo") String operNo,
                                     @RequestParam("operPwd") String operPwd,
                                     @RequestParam("captcha") String captcha,
                                     boolean rememberMe, HttpServletResponse response) throws IOException {
        log.info("登录开始，operNo：" + operNo + ",rememberMe：" + rememberMe + ",captcha:" + captcha);
        String sessionCaptcha = (String) SecurityUtils.getSubject().getSession().getAttribute(KEY_CAPTCHA);
//        if (StringUtils.isBlank(captcha) || !captcha.equalsIgnoreCase(sessionCaptcha)) {
//            return new CommonResult(ShiroResultEnum.CAPTCHA_EXCEPTION.getCode(), ShiroMessageEnum.SHIRO_CAPTCHA_EXCEPTION_MESSAGE.getMsg());
//        }
        UsernamePasswordToken token = new UsernamePasswordToken(operNo, operPwd, rememberMe);
        Subject subject = SecurityUtils.getSubject();
        log.info("登录结束！");
        return exceptionMessage(subject, token, response);
    }


    /**
     * 执行登录
     *
     * @param subject
     * @param token
     * @return
     */
    public CommonResult exceptionMessage(Subject subject, UsernamePasswordToken token, HttpServletResponse response) throws IOException {
        HttpServletRequest request = ServletUtils.getRequest();
        try {
            subject.login(token);
            SysOperator sysOperator = (SysOperator) subject.getPrincipal();
            updateLastLoginTime(sysOperator);
            //如果是外网登录，查询外网账户的企业状态
            CommonResult result = dealExtraNet(sysOperator);
            if (ShiroResultEnum.SUCCESS.getCode() != result.getData()) {
                return result;
            }
            new SimpleThreadFactory().newThread(new AsyncFactory(request, token.getUsername(), "1", ShiroMessageEnum.SHIRO_AUTHC_LOGIN_MESSAGE.getMsg())).start();
//            response.sendRedirect("http://47.105.173.243:8082/#/home/main");
            return new CommonResult(ShiroResultEnum.SUCCESS.getCode(), ShiroMessageEnum.SHIRO_AUTHC_LOGIN_MESSAGE.getMsg(), sysOperator.getCompanyNo(), token.getUsername());
        } catch (UnknownAccountException unknownAccountException) {
            new SimpleThreadFactory().newThread(new AsyncFactory(request, token.getUsername(), "0", ShiroMessageEnum.SHIRO_AUTHC_UNKNOWNACCOUNTEXCEPTION_MESSAGE.getMsg())).start();
            return new CommonResult(ShiroResultEnum.UNKNOWN_ACCOUNT_EXCEPTION.getCode(), ShiroMessageEnum.SHIRO_AUTHC_UNKNOWNACCOUNTEXCEPTION_MESSAGE.getMsg());
        } catch (IncorrectCredentialsException incorrectCredentialsException) {
            new SimpleThreadFactory().newThread(new AsyncFactory(request, token.getUsername(), "0", ShiroMessageEnum.SHIRO_AUTHC_INCORRECTCREDENTIALSEXCEPTION_MESSAGE.getMsg())).start();
            return new CommonResult(ShiroResultEnum.INCORRECT_CREDENTIALS_EXCEPTION.getCode(), ShiroMessageEnum.SHIRO_AUTHC_INCORRECTCREDENTIALSEXCEPTION_MESSAGE.getMsg());
        } catch (LockedAccountException lockedAccountException) {
            new SimpleThreadFactory().newThread(new AsyncFactory(request, token.getUsername(), "0", ShiroMessageEnum.SHIRO_AUTHC_LOCKEDACCOUNTEXCEPTION_MESSAGE.getMsg())).start();
            return new CommonResult(ShiroResultEnum.FAIL.getCode(), ShiroMessageEnum.SHIRO_AUTHC_LOCKEDACCOUNTEXCEPTION_MESSAGE.getMsg());
        } catch (DisabledAccountException disabledAccountException) {
            new SimpleThreadFactory().newThread(new AsyncFactory(request, token.getUsername(), "0", ShiroMessageEnum.SHIRO_AUTHC_DISABLEDACCOUNTEXCEPTION_MESSAGE.getMsg())).start();
            return new CommonResult(ShiroResultEnum.FAIL.getCode(), ShiroMessageEnum.SHIRO_AUTHC_DISABLEDACCOUNTEXCEPTION_MESSAGE.getMsg());
        } catch (ExcessiveAttemptsException excessiveAttemptsException) {
            new SimpleThreadFactory().newThread(new AsyncFactory(request, token.getUsername(), "0", ShiroMessageEnum.SHIRO_AUTHC_EXCESSIVEATTEMPTSEXCEPTION_MESSAGE.getMsg())).start();
            return new CommonResult(ShiroResultEnum.FAIL.getCode(), ShiroMessageEnum.SHIRO_AUTHC_EXCESSIVEATTEMPTSEXCEPTION_MESSAGE.getMsg());
        } catch (ConcurrentAccessException concurrentAccessException) {
            new SimpleThreadFactory().newThread(new AsyncFactory(request, token.getUsername(), "0", ShiroMessageEnum.SHIRO_AUTHC_CONCURRENTACCESSEXCEPTION_MESSAGE.getMsg())).start();
            return new CommonResult(ShiroResultEnum.FAIL.getCode(), ShiroMessageEnum.SHIRO_AUTHC_CONCURRENTACCESSEXCEPTION_MESSAGE.getMsg());
        } catch (AccountException accountException) {
            new SimpleThreadFactory().newThread(new AsyncFactory(request, token.getUsername(), "0", ShiroMessageEnum.SHIRO_AUTHC_ACCOUNTEXCEPTION_MESSAGE.getMsg())).start();
            return new CommonResult(ShiroResultEnum.FAIL.getCode(), ShiroMessageEnum.SHIRO_AUTHC_ACCOUNTEXCEPTION_MESSAGE.getMsg());
        } catch (UnsupportedTokenException unsupportedTokenException) {
            new SimpleThreadFactory().newThread(new AsyncFactory(request, token.getUsername(), "0", ShiroMessageEnum.SHIRO_AUTHC_UNSUPPORTEDTOKENEXCEPTION_MESSAGE.getMsg())).start();
            return new CommonResult(ShiroResultEnum.FAIL.getCode(), ShiroMessageEnum.SHIRO_AUTHC_UNSUPPORTEDTOKENEXCEPTION_MESSAGE.getMsg());
        } catch (CredentialsException credentialsException) {
            new SimpleThreadFactory().newThread(new AsyncFactory(request, token.getUsername(), "0", ShiroMessageEnum.SHIRO_AUTHC_CREDENTIALSEXCEPTION_MESSAGE.getMsg())).start();
            return new CommonResult(ShiroResultEnum.FAIL.getCode(), ShiroMessageEnum.SHIRO_AUTHC_CREDENTIALSEXCEPTION_MESSAGE.getMsg());
        } catch (AuthenticationException authenticationException) {
            new SimpleThreadFactory().newThread(new AsyncFactory(request, token.getUsername(), "0", ShiroMessageEnum.SHIRO_AUTHC_AUTHENTICATIONEXCEPTION_MESSAGE.getMsg())).start();
            return new CommonResult(ShiroResultEnum.FAIL.getCode(), ShiroMessageEnum.SHIRO_AUTHC_AUTHENTICATIONEXCEPTION_MESSAGE.getMsg());
        }
    }

    /**
     * 如果是外网登录，查询外网账户的企业状态
     *
     * @param sysOperator sysOperator
     * @return 结果
     */
    private CommonResult dealExtraNet(SysOperator sysOperator) {
        if (NumStrEnum.ZERO.getNum().equals(sysOperator.getAddType())) {
            String companyId = sysOperator.getCompanyId();
            String companyNo = sysOperator.getCompanyNo();
            if (StringUtils.isEmpty(companyId) || StringUtils.isEmpty(companyNo)) {
                return new CommonResult(ShiroResultEnum.ACCOUNT_ABNORMAL.getCode(), ShiroMessageEnum.ACCOUNT_ABNORMAL.getMsg());
            }
            if (checkCorp(companyNo)) {
                //1,企业登录检验
                BaseOrganization info = organizationMapper.getInfo(companyId);
                if (info != null) {
                    return response(info.getState(), info.getFlowState(), info.getFlowNum(), companyId, companyNo, FlowTypeEnum.ORGANIZATION_ADD.getType(),sysOperator.getOperNo());
                } else {
                    return new CommonResult(ShiroResultEnum.ACCOUNT_ABNORMAL.getCode(), ShiroMessageEnum.ACCOUNT_ABNORMAL.getMsg());
                }
            } else if (checkCommittee(companyNo)) {
                BaseOwnerCommittee info = baseOwnerCommitteeMapper.getInfo(companyId);
                if (info != null) {
                    return response(info.getState(), info.getFlowState(), info.getFlowNum(), companyId, companyNo, FlowTypeEnum.COMMITTEE_REGISTER.getType(),sysOperator.getOperNo());
                } else {
                    return new CommonResult(ShiroResultEnum.ACCOUNT_ABNORMAL.getCode(), ShiroMessageEnum.ACCOUNT_ABNORMAL.getMsg());
                }
            }
            return new CommonResult(ShiroResultEnum.ACCOUNT_ABNORMAL.getCode(), ShiroMessageEnum.ACCOUNT_ABNORMAL.getMsg());
        } else {
            return CommonResult.ResponseResult(1);
        }
    }

    /**
     * 判断返回状态
     *
     * @param state     备案状态
     * @param flowState 审核状态
     * @param flowNum   审批次数
     * @param companyId 企业ID
     * @param companyNo 登录账号类型
     * @return 结果
     */
    public CommonResult response(String state, String flowState, int flowNum, String companyId, String companyNo, String type,String operNo) {
        //未备案状态返回
        CommonResult result = notFiled(state);
        if (ShiroResultEnum.ACCOUNT_NOT_FILED.getCode() == result.getData()) {
            return result;
        }
        if (NumStrEnum.ONE.getNum().equals(flowState)) {
            //如果是审批中，查询审批的次数
            return approval(flowNum);
        } else if (NumStrEnum.THREE.getNum().equals(flowState)) {
            //如果是审批失败，查询失败原因
            return fail(companyId, type, companyNo);
        }
        return CommonResult.ResponseResult(1);
    }

    /**
     * 未备案状态返回
     *
     * @param state 备案状态
     * @return 返回结果
     */
    private CommonResult notFiled(String state) {
        if (!NumStrEnum.ONE.getNum().equals(state)) {
            return new CommonResult(ShiroResultEnum.ACCOUNT_NOT_FILED.getCode(), ShiroMessageEnum.ACCOUNT_NOT_FILED.getMsg());
        }
        return CommonResult.ResponseResult(1);
    }

    /**
     * 审核中返回
     *
     * @param flowNum 审批次数
     * @return 结果
     */
    public CommonResult approval(int flowNum) {
        if (flowNum > 1) {
            return new CommonResult(ShiroResultEnum.RE_APPROVAL.getCode(), ShiroMessageEnum.RE_APPROVAL.getMsg());
        } else {
            return new CommonResult(ShiroResultEnum.UNDER_APPROVAL.getCode(), ShiroMessageEnum.UNDER_APPROVAL.getMsg());
        }
    }

    /**
     * 审核失败时
     *
     * @param companyId 企业ID
     * @param type      审核类型
     * @param companyNo 登录用户类型
     * @return 结果
     */
    private CommonResult fail(String companyId, String type, String companyNo) {
        //如果是审批失败，查询失败原因
        String opinion = flowAuditdetailMapper.getOpinion(companyId, type);
        return new CommonResult(ShiroResultEnum.REJECTION_OF_APPROVAL.getCode(), opinion, companyNo);
    }

    /**
     * 判断是否是企业
     *
     * @param type 类型
     * @return true/false
     */
    private boolean checkCorp(String type) {
        return ("01".equals(type) || "02".equals(type) || "03".equals(type) || "04".equals(type) || "08".equals(type));
    }

    /**
     * 判断是否是业委会、居委会
     *
     * @param type 类型
     * @return true/false
     */
    private boolean checkCommittee(String type) {
        return ("05".equals(type) || "06".equals(type));
    }

    /**
     * 跟新最后登陆时间
     *
     * @param sysOperator
     */
    public void updateLastLoginTime(SysOperator sysOperator) {
        loginService.updateSysOperLastLoginTime(sysOperator.getOperNo());
    }

    /**
     * 获取验证码
     *
     * @param request
     * @param response
     */
    @RequestMapping("/Captcha.jpg")
    public void getCaptcha(HttpServletRequest request, HttpServletResponse response) {
        // 设置相应类型,告诉浏览器输出的内容为图片
        response.setContentType("image/jpeg");
        // 不缓存此内容
        response.setHeader("Pragma", "No-cache");
        response.setHeader("Cache-Control", "no-cache");
        response.setDateHeader("Expire", 0);
        try {
            HttpSession session = request.getSession();
            CaptchaUtil tool = new CaptchaUtil();
            StringBuffer code = new StringBuffer();
            BufferedImage image = tool.genRandomCodeImage(code);
            session.removeAttribute(KEY_CAPTCHA);
            session.setAttribute(KEY_CAPTCHA, code.toString());
            // 将内存中的图片通过流动形式输出到客户端
            ImageIO.write(image, "JPEG", response.getOutputStream());
        } catch (Exception e) {
            log.error("错误信息:()", e.getMessage(), e);
        }
    }

    /**
     * 外网获取验证码
     *
     * @param request
     */
    @RequestMapping("/netCaptcha.jpg")
    public String getNetCaptcha(HttpServletRequest request) {
        // 设置相应类型,告诉浏览器输出的内容为图片
        HttpSession session = request.getSession();
        CaptchaUtil tool = new CaptchaUtil();
        StringBuffer code = new StringBuffer();
        tool.genRandomCodeImage(code);
        session.removeAttribute(KEY_CAPTCHA);
        session.setAttribute(KEY_CAPTCHA, code.toString());
        return code.toString();
    }

    /**
     * 检验用户是否失效
     *
     * @return 结果
     */
    @PostMapping("/checkSession")
    public CommonResult checkSession() {
        SysOperator u = (SysOperator) ShiroUserUtil.getSubject().getPrincipal();
        if (null != u) {
            return CommonResult.ResponseResult(NumberEnum.ONE.getNum(), "可用");
        }
        return CommonResult.ResponseResult(NumberEnum.ZERO.getNum(), "失效");
    }

    /**
     * 外网查看验证码填写是否正确
     */
    @PostMapping("/verificationCode")
    public CommonResult verificationCode(String code) {
        String sessionCaptcha = (String) SecurityUtils.getSubject().getSession().getAttribute(KEY_CAPTCHA);
        if (StringUtils.isBlank(code) || !code.equalsIgnoreCase(sessionCaptcha)) {
            return new CommonResult(ShiroResultEnum.CAPTCHA_EXCEPTION.getCode(), ShiroMessageEnum.SHIRO_CAPTCHA_EXCEPTION_MESSAGE.getMsg());
        }
        return CommonResult.ResponseResult(1);
    }
}

class SimpleThreadFactory implements ThreadFactory {
    @Override
    public Thread newThread(Runnable r) {
        return new Thread(r);
    }
}